﻿#include "hmi_element.h"
#include <QPainter>
#include <QGraphicsSceneMouseEvent>
#include <QMenu>
#include <QGraphicsScene>
#include <QColorDialog>
#include <QInputDialog>
#include <QDialog>
#include <QSpinBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGraphicsView>

HmiComponent::HmiComponent(QGraphicsItem *parent) : QGraphicsObject(parent)
{
    // 允许用户通过鼠标拖动该组件在 QGraphicsScene 中移动
    setFlag(QGraphicsItem::ItemIsMovable, true);
    // 启用可选中属性，允许用户通过鼠标点击选中该组件
    setFlag(QGraphicsItem::ItemIsSelectable, true);
    // 启用悬停事件响应，接收鼠标悬停相关的事件，缩放图形时
    setAcceptHoverEvents(true);
    // 缓存渲染结果，提升绘制效率，减少重复绘制的过程
    setCacheMode(QGraphicsItem::DeviceCoordinateCache);
}

// 设置组件的显示颜色，在颜色发生变化时触发重绘
void HmiComponent::setColor(const QColor& color)
{
    if (color_ != color)
    {
        color_ = color;
        update();
    }
}

// 获取当前组件的显示颜色
QColor HmiComponent::color() const
{
    return color_;
}

// 设置组件的名称
void HmiComponent::setComponentName(const QString &name)
{
    componentName_ = name;
}

// 返回当前组件的名称
QString HmiComponent::componentName() const
{
    return componentName_;
}

// 设置组件在 ON状态 下应显示的颜色
void HmiComponent::setOnColor(const QColor &color)
{
    onColor_ = color;
}

// 返回组件 ON状态 的颜色配置
QColor HmiComponent::onColor() const
{
    return onColor_;
}

// 设置组件在 OFF状态 下应显示的颜色
void HmiComponent::setOffColor(const QColor &color)
{
    offColor_ = color;
    // 默认展示 OFF 状态颜色
    if (color_ == offColor_)
    {
        setColor(color);
    }
}

// 返回组件 OFF状态 的颜色配置
QColor HmiComponent::offColor() const
{
    return offColor_;
}

// 设置组件的地址
void HmiComponent::setAddress(int address)
{
    if (address_ != address) {
        int oldAddress = address_;
        address_ = address;
        Q_EMIT coilAddressChanged(oldAddress, address);
        update();
    }
}

// 返回组件当前的地址值
int HmiComponent::address() const
{
    return address_;
}

// 设置组件的宽度和高度
void HmiComponent::setSize(double width, double height)
{
    // Qt 图形框架的要求，组件边界矩形boundingRect变化前通知场景
    prepareGeometryChange();
    width_ = width;
    height_ = height;
    update();
}

// 返回组件当前的尺寸，封装为 QSizeF 对象
QSizeF HmiComponent::size() const
{
    return QSizeF(width_, height_);
}

// 绘图函数，绘制组件标签、调用派生类的 paintShape、
void HmiComponent::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    // 未使用的参数，防止警告
    Q_UNUSED(option);
    Q_UNUSED(widget);
    // 启用抗锯齿渲染，提高视觉效果
    painter->setRenderHint(QPainter::Antialiasing);
    // 保存初始状态，避免绘制标签后影响后续的组件绘制
    painter->save();
    // 黑色画笔，宽度1
    QFont font = painter->font();
    font.setPointSize(8);  // 设置固定字体大小
    painter->setFont(font);
    painter->setPen(QPen(Qt::black, 1));
    // 计算文本所需的最小宽度
    const QString text = QString("M") + QString::number(address_);
    QFontMetrics fm(font);
    int textWidth = fm.horizontalAdvance(text);

    // 确保标签区域至少能完整显示文本
    double labelWidth = qMax(width_, static_cast<double>(textWidth + 4)); // 添加4像素边距

    // 计算标签区域的起始位置，使其在组件上方居中
    double labelX = (width_ - labelWidth) / 2.0;

    const QRectF topRect(labelX, -labelHeight, labelWidth, labelHeight);
    painter->drawText(topRect, Qt::AlignHCenter | Qt::AlignVCenter, text);
    // 恢复到 save() 前的状态
    painter->restore();
    // 多态调用
    paintShape(painter);
    // 组件被选中时
    if (isSelected())
    {
        // 不填充内部、虚线、设置边框范围
        painter->setBrush(Qt::NoBrush);
        painter->setPen(QPen(Qt::darkGray, 2, Qt::DashLine));
        painter->drawRect(QRectF(0, 0, width_, height_));
        // 绘制缩放控制块，8*8小方块
        QRectF rect = QRectF(0, 0, width_, height_); // 主体矩形
        painter->setBrush(Qt::white);
        painter->setPen(QPen(Qt::black, 1));
        painter->drawRect(rect.right() - 8, rect.bottom() - 8, 8, 8);
    }
}

// 包含标签(M + 地址)的边界矩形
QRectF HmiComponent::boundingRect() const
{
    // 计算文本所需的最小宽度
    QFont font;
    font.setPointSize(8);
    QFontMetrics fm(font);
    int textWidth = fm.horizontalAdvance(QString("M") + QString::number(address_));
    // 标签区域宽度取组件宽度和文本所需宽度的较大值
    double labelWidth = qMax(width_, static_cast<double>(textWidth + 4));
    // 计算标签区域的起始位置
    double labelX = (width_ - labelWidth) / 2.0;
    return QRectF(labelX, -labelHeight, labelWidth, height_ + labelHeight);
}

// 对鼠标按下事件的处理
void HmiComponent::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    // 检查是否点击了缩放控制点
    if (event->button() == Qt::LeftButton && isSelected())
    {
        QRectF rect = QRectF(0, 0, width_, height_);
        QRectF handleRect(rect.right() - 8, rect.bottom() - 8, 8, 8);
        // 检查鼠标按下位置是否在缩放控制点内
        if (handleRect.contains(event->pos()))
        {
            // 标记为正在缩放状态
            resizing_ = true;
            // 记录缩放开始时的场景坐标
            resizeStartPos_ = event->scenePos();
            // 记录缩放开始时的组件尺寸
            resizeStartSize_ = QSizeF(rect.width(), rect.height());
            // 接受该事件，不会被父类或其他对象重复处理
            event->accept();
            return;
        }
    }
    // 调用父类的事件处理函数，确保基础逻辑执行
    QGraphicsObject::mousePressEvent(event);
}

// 创建颜色预览标签
QLabel* HmiComponent::colorDisplayLabel(const QColor& color)
{
    // 创建预览标签大小，允许标签填充背景色
    QLabel* label = new QLabel;
    label->setFixedSize(40, 20);
    label->setAutoFillBackground(true);
    QPalette palette = label->palette();
    // 填充整个背景的颜色为color
    palette.setColor(QPalette::Window, color);
    // 将调色板应用于label
    label->setPalette(palette);
    label->setFrameShape(QFrame::Box);
    return label;
}

// 显示属性对话框
void HmiComponent::propertyDialog()
{
    QDialog dialog;
    dialog.setWindowTitle(u8"属性");

    // 地址控件
    QLabel* addressLabel = new QLabel(u8"线圈: M");
    QSpinBox* addressSpin = new QSpinBox();
    addressSpin->setRange(0, 4000);
    addressSpin->setFixedWidth(150);
    addressSpin->setValue(address_);

    // OFF状态颜色控件
    QLabel* offColorLabel = new QLabel(u8"OFF状态外观: ");
    QLabel* offColorDisplay = colorDisplayLabel(offColor_);
    QPushButton* offColorBtn = new QPushButton(u8"选择颜色");

    // 地址行布局
    QHBoxLayout* addressLayout = new QHBoxLayout();
    addressLayout->addWidget(addressLabel);
    addressLayout->addWidget(addressSpin);
    // 对指示灯显示
    QHBoxLayout* onColorLayout = nullptr;
    if (!dynamic_cast<HmiButton*>(this)) {
        QLabel* onColorLabel = new QLabel(u8"ON状态外观: ");
        QLabel* onColorDisplay = colorDisplayLabel(onColor_);
        QPushButton* onColorBtn = new QPushButton(u8"选择颜色");
        connect(onColorBtn, &QPushButton::clicked, [&]() {
            QColor color = QColorDialog::getColor(onColor_, nullptr, u8"选择 ON 状态颜色");
            if (color.isValid())
            {
                onColor_ = color;
                QPalette pal = onColorDisplay->palette();
                pal.setColor(QPalette::Window, color);
                onColorDisplay->setPalette(pal);
                onColorDisplay->update();
            }
        });
        onColorLayout = new QHBoxLayout();
        onColorLayout->addWidget(onColorLabel);
        onColorLayout->addWidget(onColorDisplay);
        onColorLayout->addWidget(onColorBtn);
    }
    // OFF颜色行布局
    QHBoxLayout* offColorLayout = new QHBoxLayout();
    offColorLayout->addWidget(offColorLabel);
    offColorLayout->addWidget(offColorDisplay);
    offColorLayout->addWidget(offColorBtn);
    // 确定/取消按钮行布局
    QHBoxLayout* buttonLayout = new QHBoxLayout();
    QPushButton* okButton = new QPushButton(u8"确定");
    QPushButton* cancelButton = new QPushButton(u8"取消");
    buttonLayout->addWidget(okButton);
    buttonLayout->addWidget(cancelButton);
    // 主布局：将上述所有行布局纵向排列
    QVBoxLayout* mainLayout = new QVBoxLayout(&dialog);
    mainLayout->addLayout(addressLayout);
    if (onColorLayout) mainLayout->addLayout(onColorLayout);
    mainLayout->addLayout(offColorLayout);
    mainLayout->addLayout(buttonLayout);

    // OFF颜色按钮点击事件
    connect(offColorBtn, &QPushButton::clicked, [&]() {
        QColor color = QColorDialog::getColor(offColor_, nullptr, u8"选择 OFF 状态颜色");
        if (color.isValid())
        {
            offColor_ = color;
            QPalette pal = offColorDisplay->palette();
            pal.setColor(QPalette::Window, color);
            offColorDisplay->setPalette(pal);
            offColorDisplay->update();
        }
    });
    // 确定按钮点击事件
    connect(okButton, &QPushButton::clicked, [&]() {
        int newAddress = addressSpin->value();
        setAddress(newAddress);

        // 更新显示颜色
        setColor(offColor_);

        // 如果是指示灯，根据当前状态更新颜色
        if (auto indicator = dynamic_cast<HmiIndicator*>(this)) {
            // 使用当前状态对应的颜色更新显示
            indicator->setColor(indicator->isOn() ? onColor_ : offColor_);
        }

        update();
        dialog.accept();
    });
    // 取消按钮：放弃修改并关闭对话框
    connect(cancelButton, &QPushButton::clicked, &dialog, &QDialog::reject);
    // 显示对话框，直到用户关闭
    dialog.exec();
}

// 处理鼠标右键菜单
void HmiComponent::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
    // 清除场景中其他组件的选中状态
    scene()->clearSelection();
    // 将当前右键点击的组件设为选中状态
    setSelected(true);

    QMenu menu;
    QAction *property = menu.addAction(u8"属性");
    // 菜单分隔线
    menu.addSeparator();
    QAction *copy = menu.addAction(u8"复制");
    QAction *del = menu.addAction(u8"删除");
    // 点击属性后的属性设置对话框
    connect(property, &QAction::triggered, this, &HmiComponent::propertyDialog);
    // 点击 复制/删除 时，发射对应信号，传递当前组件指针
    connect(copy, &QAction::triggered, this, [this]() {
        Q_EMIT copyRequested(this);
    });
    connect(del, &QAction::triggered, this, [this]() {
        Q_EMIT deleteRequested(this);
    });
    // 在鼠标右键点击的屏幕位置显示菜单
    menu.exec(event->screenPos());
}

// 处理组件缩放和拖动的功能
void HmiComponent::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    // 拖动组件右下角的缩放控制点时，调整组件大小
    if (resizing_)
    {
        // 计算尺寸变化（当前位置 - 缩放开始时的位置）
        QPointF change = event->scenePos() - resizeStartPos_;
        // 限制大小
        double newWidth = qMax(20.0, resizeStartSize_.width() + change.x());
        double newHeight = qMax(20.0, resizeStartSize_.height() + change.y());
        // 更新组件大小
        setSize(newWidth, newHeight);
        // 标记事件已处理
        event->accept();
        return;
    }
    // 检查是否为左键按下
    if (!(event->buttons() & Qt::LeftButton))
    {
        QGraphicsObject::mouseMoveEvent(event);
        return;
    }
    // 判断当前组件是否设置了可移动标志
    if (!(flags() & QGraphicsItem::ItemIsMovable))
    {
        QGraphicsObject::mouseMoveEvent(event);
        return;
    }

    // 计算鼠标移动（当前位置 - 上一次移动的位置）
    QPointF change = event->scenePos() - event->lastScenePos();
    // 计算拖动后的新位置（当前位置 + 移动数据）
    QPointF newPos = pos() + change;

    // 获取视图，检查是否有视图
    if (scene()->views().isEmpty())
    {
        QGraphicsObject::mouseMoveEvent(event);
        return;
    }
    QGraphicsView* view = scene()->views().first();
    // 获取视图的可见区域
    QRect viewPortRect = view->viewport()->rect();

    // 将可见区域矩形映射到场景坐标
    QPointF topLeftScene = view->mapToScene(viewPortRect.topLeft());
    QPointF bottomRightScene = view->mapToScene(viewPortRect.bottomRight());
    QRectF visibleSceneRect(topLeftScene, bottomRightScene);

    // 组件的边界矩形（局部坐标）
    QRectF bounds = boundingRect();

    // 计算移动后组件边界矩形（在场景坐标）
    QRectF newBounds = bounds.translated(newPos);

    // 限制组件边界必须完全在视图可见区域内
    double limitedX = newPos.x();
    double limitedY = newPos.y();
    if (newBounds.left() < visibleSceneRect.left())
    {
        limitedX += visibleSceneRect.left() - newBounds.left();
    }
    if (newBounds.top() < visibleSceneRect.top())
    {
        limitedY += visibleSceneRect.top() - newBounds.top();
    }
    if (newBounds.right() > visibleSceneRect.right())
    {
        limitedX -= newBounds.right() - visibleSceneRect.right();
    }
    if (newBounds.bottom() > visibleSceneRect.bottom())
    {
        limitedY -= newBounds.bottom() - visibleSceneRect.bottom();
    }

    setPos(QPointF(limitedX, limitedY));
    event->accept();
}

// 处理鼠标释放事件
void HmiComponent::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    // 若当前处于缩放状态，且释放的是左键
    if (resizing_ && event->button() == Qt::LeftButton)
    {
        // 结束缩放状态
        resizing_ = false;
        event->accept();
        return;
    }
    // 若不是缩放状态，交给父类处理默认行为
    QGraphicsObject::mouseReleaseEvent(event);
}

// 处理鼠标悬停移动事件
void HmiComponent::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
    QRectF rect = QRectF(0, 0, width_, height_);
    // 定义右下角的缩放控制块
    QRectF handleRect(rect.right() - 8, rect.bottom() - 8, 8, 8);
    // 若组件被选中，且鼠标悬停在缩放控制块上
    if (isSelected() && handleRect.contains(event->pos()))
    {
        // 切换光标为对角缩放样式
        setCursor(Qt::SizeFDiagCursor);
    }
    else
    {
        // 恢复默认箭头光标
        setCursor(Qt::ArrowCursor);
    }
    // 调用父类默认实现
    QGraphicsObject::hoverMoveEvent(event);
}

HmiButton::HmiButton(QGraphicsItem *parent) : HmiComponent(parent), isOn_(false)
{
    width_ = 65.0;
    height_ = 30.0;
    color_ = Qt::gray;
    offColor_ = color_;
    // onColor_ = Qt::green;
    componentName_ = "Button";
    setColor(offColor_);
}

// 获取按钮当前状态
bool HmiButton::isOn() const
{
    return isOn_;
}

// 设置按钮的ON/OFF状态，并更新显示
void HmiButton::setOn(bool on)
{
    if (isOn_ != on)
    {
        isOn_ = on;
        Q_EMIT stateChanged(isOn_);
        update();
    }
}

// 绘制按钮的形状，由基类paint调用
void HmiButton::paintShape(QPainter *painter)
{
    // 取消边框
    painter->setPen(Qt::NoPen);
    // 始终用当前颜色
    painter->setBrush(color_);
    // 绘制矩形作为按钮主体
    painter->drawRect(QRectF(0, 0, width_, height_));
}

void HmiButton::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    // 只在左键按下且未进行缩放操作时处理
    if (event->button() == Qt::LeftButton && !resizing_)
    {
        pressed_ = true;
        isClicked_ = true; // 标记可能需要处理点击
        // 按下时将 OFF 颜色加深
        setColor(offColor_.darker(120));
        update();
    }
    HmiComponent::mousePressEvent(event);
}

void HmiButton::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    // 如果移动距离超过设定值，取消点击处理
    double dx = qAbs(event->pos().x() - event->buttonDownPos(Qt::LeftButton).x());
    double dy = qAbs(event->pos().y() - event->buttonDownPos(Qt::LeftButton).y());
    if (isClicked_ && (dx > 8 || dy < 8))
    {
        isClicked_ = false;
    }

    HmiComponent::mouseMoveEvent(event);
}

void HmiButton::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    // 视觉恢复
    pressed_ = false;
    setColor(offColor_);
    update();

    // 只有在应该处理点击且不是缩放操作时才切换状态
    if (isClicked_ && event->button() == Qt::LeftButton && !resizing_)
    {
        // 切换状态
        setOn(!isOn_);
    }

    isClicked_ = false; // 重置标志

    HmiComponent::mouseReleaseEvent(event);
}

HmiIndicator::HmiIndicator(QGraphicsItem *parent) : HmiComponent(parent)
{
    width_ = 30.0;
    height_ = 30.0;
    color_ = Qt::red;
    offColor_ = color_;
    onColor_ = Qt::green;
    componentName_ = "Indicator";
    setColor(offColor_);
}

bool HmiIndicator::isOn() const
{
    return color() == onColor();
}

// 绘制指示灯的形状
void HmiIndicator::paintShape(QPainter *painter)
{
    painter->setPen(Qt::NoPen);
    painter->setBrush(color_);
    painter->drawEllipse(QRectF(0, 0, width_, height_));
}

